using MongoDB.Bson;
using MongoDB.Driver;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ScrapeSefariaMongoDBinDotNet
{
    public partial class Form1 : Form
    {
        static async Task MainAsync1()
        {
            string col_name = "texts";
            IMongoCollection<BsonDocument> books_collection = db.GetCollection<BsonDocument>(col_name);
            long total_docs = books_collection.CountDocuments(_ => true);
            Console.WriteLine($"Expecing {total_docs} documents.");

            Stopwatch timer = new Stopwatch();
            timer.Start();

            int n_books = 0;
            int skipped_books = 0;
            Hashtable books_ht = new Hashtable();
            Hashtable lang_ht = new Hashtable();

            using (IAsyncCursor<BsonDocument> cursor = await books_collection.FindAsync(new BsonDocument()))
            {
                while (await cursor.MoveNextAsync())
                {
                    IEnumerable<BsonDocument> batch = cursor.Current;
                    foreach (BsonDocument document in batch)
                    {
                        if (((string)document["title"]).Contains("."))
                        {
                            throw new Exception("No periot (.) is expected in the Title.");
                        }
                        add_and_count_key_to_ht(lang_ht, (string)document["language"]);
                        //
                        string book_key = (string)document["versionTitle"] + " - " + (string)document["title"] + " - " + (string)document["language"];
                        if (!books_ht.ContainsKey(book_key))
                        {
                            books_ht.Add(book_key, document);
                        }
                        else
                        {
                            var existiong_doc = books_ht[book_key];
                        }
                        string versionTitleInHebrew = null;
                        if (document.Contains("versionTitleInHebrew"))
                        {
                            versionTitleInHebrew = (string)document["versionTitleInHebrew"];
                        }
                        n_books++;
                        string[] ignore_heb_titles = { "ווילנא, האלמנה והאחים ראם, תרל\"ח" };
                        if (ignore_heb_titles.Contains(versionTitleInHebrew))
                        {
                            skipped_books++;
                            continue;
                        }
                        Console.WriteLine($"book: {n_books}: {book_key}");
                    }
                }
            }

            timer.Stop();
            MessageBox.Show($"Congradulations! you just succsessfuly scanned '{db_name}' db, collection: '{col_name}', total: {n_books} Documents (skipped: {skipped_books}) in {timer.Elapsed.ToString(@"mm\:ss")}"); // \:fff

        }

        private void button1_Click(object sender, EventArgs e)
        {
            MainAsync1();
            // "chapter" in book.
        }

        private static string get_ref_unused(string book_name, int chap_n, int verse_n, ref int n_err, BsonDocument document)
        {
            BsonArray ref_ar = (BsonArray)document["refs"];
            if (ref_ar.Count != 2)
            {
                throw new Exception();
            }
            int ii = 0;
            string r0 = (string)ref_ar[0];
            string r1 = (string)ref_ar[1];
            string src_full = $"{book_name} {chap_n}:{verse_n}";
            string src_part = $"{book_name} {chap_n}";
            if (r0 == src_full)
            {
                ii = 1; // expandedRefs0 referencing?
            }
            else if (r1 == src_full)
            {
                ii = 0; // expandedRefs1 translating
            }
            else if (r0 == src_part)
            {
                ii = 1;
            }
            else if (r1 == src_part)
            {
                ii = 0;
            }
            else if (r0.StartsWith(src_part))
            {
                // Warning
                ii = 1;
            }
            else if (r1.StartsWith(src_part))
            {
                // Warning
                ii = 0; // expandedRefs1 translating
            }
            else
            {
                n_err++;
                return null;
            }
            string _ref = (string)ref_ar[ii];
            return _ref;
        }

        const string book_re = "[\\w\\s\\(\\)';]+";
        private static string get_content(string book_name, int chap_n, int verse_n, string verse_str, ref int n_err, Hashtable ref_books_ht, string _ref)
        {
            Match m = Regex.Match(_ref, "^(?<b>" + book_re + @")(,\s(?<c1>[\w\s'\(\)\[\];]+))?(,\s(?<c2>[\w\s'\(\);!?""]+))?(,\s(?<c3>[\w\s'\(\)]+))?(,\s(?<c4>[\w\s'\(\)]+))?(,\s(?<c5>[\w\s'\(\)]+))?\s(?<r0>[\d\-]+)[ab]?(:(?<r1>[\d]+)[\d\-ab]*(:(?<r2>[\d]+)[\d\-]*(:(?<r3>[\d]+)[\d\-]*)?)?)?$");
            if (!m.Success)
            {
                n_err++;
                return null;
                throw new Exception();
            }

            var ref_book_name = m.Groups["b"].Value;
            add_and_count_key_to_ht(ref_books_ht, ref_book_name);
            add_and_count_key_to_ht(bible_ref_books_ht, ref_book_name);
            string[] indexes = new string[4];
            int n_indexes = 0;
            for (; n_indexes < indexes.Length; n_indexes++)
            {
                indexes[n_indexes] = m.Groups["r" + n_indexes].Value;
                if (indexes[n_indexes] == "")
                {
                    break;
                }
            }

            string[] chapters = new string[5];
            for (int n_chapters = 0; n_chapters < chapters.Length; n_chapters++)
            {
                chapters[n_chapters] = m.Groups["c" + (n_chapters + 1)].Value;
                if (chapters[n_chapters] == "")
                {
                    break;
                }
            }

            FilterDefinition<BsonDocument> filter2 = "{ title: \"" + ref_book_name + "\" }"; // , language: 'he'
            var books_list = texts_collection.Find(filter2).Sort("{ language: -1, licenseVetted: -1, priority: -1 }").ToList();

            BsonValue content = null;

            int ref_book_i = 0;
            for (; ref_book_i < books_list.Count; ref_book_i++)
            {
                var ref_book = books_list[ref_book_i];
                content = ref_book["chapter"];
                //var x = ref_book["versionTitle"];
                //continue;
                int chapter_i = 0;
                while (true)
                {
                    if (chapters[chapter_i] == "")
                    {
                        break;
                    }
                    string chap_title = chapters[chapter_i];
                    if (!((BsonDocument)content).Contains(chap_title))
                    {
                        content = null;
                        break;
                    }
                    content = content[chap_title];
                    chapter_i++;
                }

                if (content == null)
                {
                    add_and_count_key_to_ht(total_ref_books_missed_ht, ref_book_name);
                    n_err++;
                    continue;
                }

                if (content.GetType() != typeof(BsonArray))
                {
                    content = content["default"];
                }
                if (content.GetType() != typeof(BsonArray))
                {
                    throw new Exception();
                }
                else
                {
                    for (int index_i = 0; index_i < n_indexes; index_i++)
                    {
                        string index = indexes[index_i];
                        int dash_i = index.IndexOf("-"); // todo.
                        if (dash_i > 0)
                        {
                            index = index.Substring(0, dash_i);
                        }
                        int content_i = int.Parse(index) - 1;
                        if (content.GetType() != typeof(BsonArray) && index_i == n_indexes - 1)
                        {
                            break;
                        }
                        var n = ((BsonArray)content).Count;
                        if (n <= content_i)
                        {
                            if (index_i == n_indexes - 1 && n > 0)
                            {
                                content = content[n - 1];
                            }
                            else
                            {
                                content = null;
                            }
                            break;
                        }
                        content = content[content_i];
                    }
                }

                if (content == null)
                {
                    continue;
                }

                if (ref_book["language"] != "he" && !(new string[] { "Legends of the Jews", "Rav Hirsch on Torah", "Kitzur Baal Haturim on ", "Rabbeinu Chananel on ", "Sforno on ", "Daat Zkenim on ", "Saadia Gaon on " }).Any(ref_book_name.StartsWith) && !(new string[] { }.Contains(_ref)))
                {
                    // Warning: reference not in Hebrew.
                }
                if (torah_books_ht.Contains(ref_book_name) || nach_books_ht.Contains(ref_book_name))
                {
                    add_and_count_key_to_ht(ref_bible_ht, ref_book_name);
                    string v = get_verse_with_nikkud(book_name, chap_n - 1, verse_n - 1);
                    string v2 = "";
                    Match _m = Regex.Match(_ref, @"^(?<b>.+) (?<c>[\d]+):(?<v>[\d]+)");
                    if (_m.Success)
                    {
                        v2 = get_verse_with_nikkud(_m.Groups["b"].Value, int.Parse(_m.Groups["c"].Value) - 1, int.Parse(_m.Groups["v"].Value) - 1);
                    }
                    if (false)
                    {
                        Console.WriteLine($"{verse_str} {v} - {_ref}: {v2}"); // ({doc_timer.Elapsed.ToString(@"mm\:ss")}): 
                    }
                }
                else
                {
                    add_and_count_key_to_ht(bible_ref_books_ht, ref_book_name);
                    if (!torah_ref_books_ht.Contains(ref_book_name))
                    {
                        add_and_count_key_to_ht(nach_ref_books_ht, ref_book_name);
                    }
                }
                break;
            }

            if (content == null && (new string[] { "JPS 1985 Footnotes", "Ramban on ", "Ibn Ezra on ", "Malbim on " }).Any(ref_book_name.StartsWith) && !(new string[] { "The Midrash of Philo 5:11:1-4", "Mishneh Torah, Repentance 6:3", "Mishneh Torah, Foreign Worship and Customs of the Nations 1:1-3", "Siftei Chakhamim, Leviticus 8:13:1", "Rashbam on Genesis 4:26:1" }).Contains(_ref))
            {
                return "-1";
                // throw new Exception();
            }

            if (content == null)
            {
                n_err++;
                return null;
            }

            return content.ToString();
        }

        /* */

        public static void add_and_count_key_to_ht(Hashtable ht, string key)
        {
            if (!ht.Contains(key))
            {
                ht.Add(key, 1);
            }
            else
            {
                ht[key] = (int)(ht[key]) + 1;
            }
        }



        private async Task MainAsync_unused()
        {
            return;


            //Stopwatch timer = new Stopwatch();
            //timer.Start();

            //long total_docs = collection.CountDocuments(_ => true);

            //Console.WriteLine($"Expecing {total_docs} documents.");

            //int n_books = 0;

            //IAsyncCursor<BsonDocument> cursor = collection.FindSync(_ => true);

            //await cursor.ForEachAsync(document =>
            //{
            //    n_books++;
            //    Console.WriteLine("book: " + n_books);
            //    if (total_docs == n_books)
            //    {
            //        timer.Stop();
            //        MessageBox.Show($"Congradulations! you just succsessfuly scanned all '{db_name}' db, collection: '{col_name}', total: {n_books} Document's in {timer.Elapsed.ToString(@"mm\:ss")}"); // \:fff
            //        //return;
            //    }
            //});


            string h = "";
            //var x = collection.Find(_ => true).ToList();
            //foreach (var y in x)
            //{

            //}



            //do
            //{
            //} while (cursor.MoveNext());


            //using (IAsyncCursor<BsonDocument> cursor = collection.FindSync(new BsonDocument()))
            //{
            //    while (await cursor.MoveNextAsync())
            //    {
            //        if (cursor.Current == null)
            //        {
            //            continue;
            //        }

            //        IEnumerable<BsonDocument> batch = cursor.Current;
            //        foreach (BsonDocument document in batch)
            //        {
            //            n_books++;
            //            Console.WriteLine("book: " + n_books);
            //            //Console.WriteLine(document);
            //            //Console.WriteLine();
            //        }
            //    }
            //}



            //dbClient = new MongoClient("mongodb://localhost:27017");
            //MainAsync();

            //if (false)
            //{
            //    db_name = "Books";
            //    col_name = "TNC";
            //}

            //MainAsync_1().Wait();
            //MainAsync_2();


            //string h = "debug";


            //IMongoDatabase db = dbClient.GetDatabase("Books");
            //IMongoCollection<BsonDocument> collection = db.GetCollection<BsonDocument>("TNC");
            //var documents = collection.Find(new BsonDocument()).ToList();


            //MongoClientSettings settings = new MongoClientSettings();
            //settings.Server = new MongoServerAddress("localhost", 27017);
            //settings.AllowInsecureTls = false;
            //MongoClient mongoClient = new MongoClient(settings);

            //MongoClient mongoClient = new MongoClient("mongodb://aryeh:1234@localhost:27017/TNC"); // /?connect=replicaSet


            //return;




            //MongoClient mongoClient = new MongoClient("mongodb://localhost:27017/TNC");


            //var dbList = mongoClient.ListDatabases().ToList();


            //MongoClient client = new MongoClient("mongodb://localhost:27017/");
            //IMongoDatabase db = client.GetDatabase("sefria");
            //IMongoCollection<BsonDocument> collection = db.GetCollection<BsonDocument>("texts");


            //using (IAsyncCursor<BsonDocument> cursor = await collection.FindAsync(new BsonDocument()))
            //{
            //    while (await cursor.MoveNextAsync())
            //    {
            //        IEnumerable<BsonDocument> batch = cursor.Current;
            //        foreach (BsonDocument document in batch)
            //        {
            //            Console.WriteLine(document);
            //            Console.WriteLine();
            //        }
            //    }
            //}


            //texts_col.Find(_ => true);
            ////.ToList().ForEach(vault => {
            ////    string d = "";
            ////    //Iteration    
            ////});


            //foreach (var i in texts_col.Find(Builders<BsonDocument>.Filter.Empty).ToListAsync().Result)
            //{

            //}








            //var settings1 = MongoClientSettings.FromUrl(MongoUrl.Create("mongodb://localhost:27017"));
            //var client1 = new MongoClient(settings1);

            // dbClient = new MongoClient("mongodb://localhost:27017");

            //return;

            //var dbList = dbClient.ListDatabases().ToList();

            //IMongoDatabase db = dbClient.GetDatabase(db_name);
            //IMongoCollection<BsonDocument> collection = db.GetCollection<BsonDocument>(col_name);

            //var documents = collection.Find(new BsonDocument()).ToList();

            //var filter2 = new BsonDocument("b", 0);
            //var firstDocument = collection.Find(filter2).FirstOrDefault();

            //FilterDefinition<BsonDocument> filter = FilterDefinition<BsonDocument>.Empty;
            //FindOptions<BsonDocument> options = new FindOptions<BsonDocument>
            //{
            //    BatchSize = 2,
            //    NoCursorTimeout = false
            //};

            //using (IAsyncCursor<BsonDocument> cursor = await collection.FindAsync(filter, options))
            //{
            //    while (await cursor.MoveNextAsync())
            //    {
            //        IEnumerable<BsonDocument> batch = cursor.Current;
            //        foreach (BsonDocument document in batch)
            //        {
            //            Console.WriteLine(document);
            //            Console.WriteLine();
            //        }
            //    }
            //}



            //var cursor = await collection.FindAsync(_ => true);

            //string x = "";

            //using ()
            //{
            //    while (await cursor.MoveNextAsync())
            //    {
            //    }
            //}




            //var cursor = await collection.FindAsync(FilterDefinition<BsonDocument>.Empty);

            //while (await cursor.MoveNextAsync())
            //{
            //    IEnumerable<BsonDocument> batch = cursor.Current;
            //    foreach (BsonDocument document in batch)
            //    {
            //        Console.WriteLine(document);
            //        Console.WriteLine();
            //    }
            //}

            //using (IAsyncCursor<BsonDocument> cursor = await collection.FindAsync(FilterDefinition<BsonDocument>.Empty)) /* new BsonDocument(), _=>true new BsonDocument() */
            //{
            //}
        }

        //private void button1_Click(object sender, EventArgs e)
        //{

        //    //var mongoClient3 = new MongoClient("mongodb://localhost:27017");
        //    //var db3 = mongoClient3.GetDatabase($"safria");
        //    //var numberOfDocuments = db3.GetCollection<BsonDocument>($"{"texts"}")
        //    //    .CountDocumentsAsync(new BsonDocument()).Result;


        //    // MainAsync_2(); // .Wait();
        //    return;






        //IMongoCollection<BsonDocument> collection = db.GetCollection<BsonDocument>(col_name);


        //List<BsonDocument> documents = collection.Find(new BsonDocument()).ToList();
        //IFindFluent<BsonDocument, BsonDocument> filtered_docs = collection.Find(_ => _["b"] == 1);

        //timer.Stop();
        //MessageBox.Show($"got {documents.Count} Documents. took: {timer.Elapsed.ToString(@"mm\:ss\:fff")}.");
        //}
    }
}
